package com.gelsanalyzer.mvc.controller;

import java.beans.*;
import java.lang.reflect.*;
import java.util.*;

import org.apache.log4j.*;

import com.gelsanalyzer.mvc.model.*;
import com.gelsanalyzer.mvc.view.*;

public abstract class AbstractController implements IController {

    private static final Logger log = Logger
        .getLogger(AbstractController.class);

    // Might have a parent controller
    protected IController parent;
    // Might have children controllers as well
    protected List<IController> children;

    // Store the model and the view to create the triad...
    protected IView view;
    protected AbstractModel model;

    public AbstractController(IView view, AbstractModel model) {
        this(null, view, model);
        log.info("In the 2 arg constructor...");
    }

    public AbstractController(IController parent,
                              IView view,
                              AbstractModel model) {
        this.parent = parent;
        this.view = view;
        this.model = model;
        log.info("In the 3 arg constructor...");

        // Now we need to get the wiring taken care of...
        wireController();
    }

    private void wireController() {
        if ( model != null ) model.addPropertyChangeListner(this);
    }

    public IController getParent() {
        return parent;
    }

    public void setParent(IController controller) {
        this.parent = controller;
    }

    public IView getView() {
        return view;
    }

    public void setView(IView view) {
        this.view = view;
    }

    public AbstractModel getModel() {
        return model;
    }

    public void setModel(AbstractModel model) {
        if ( this.model != null ) {
            this.model.removePropertyChangeListener(this);
        }
        this.model = model;
        this.model.addPropertyChangeListner(this);
    }

    public void addChild(IController controller) {
        if ( children == null ) {
            children = new ArrayList<IController>();
        }
        controller.setParent(this);
        controller.getModel().initData();
        children.add(controller);
    }

    public void addChildren(List<IController> controllers) {
        for ( IController controller : controllers ) {
            addChild(controller);
        }
    }
    
    public List<IController> getChildren() {
        return children;
    }

    /**
     * This method is used to implement the PropertyChangeListener interface.
     * Any model changes will be sent to this controller through the use of this
     * method.
     * 
     * @param evt An object that describes the model's property change.
     */
    public void propertyChange(PropertyChangeEvent evt) {
        view.modelPropertyChange(evt);

        if ( children != null ) {
            log
                .debug("children were NOTNULL call controller.propertyChange...");
            for ( IController controller : children ) {
                controller.propertyChange(evt);
            }
        }
    }

    /**
     * Convenience method that subclasses can call upon to fire off property
     * changes back to the models. This method used reflection to inspect each
     * of the model classes to determine if it is the owner of the property in
     * question. If it isn't, it throws a NoSuchMethodException (which the
     * method ignores).
     * 
     * @param propertyName The name of the property
     * @param newValue An object that represents the new value of the property.
     */
    protected void setModelProperty(String propertyName, Object newValue) {
        log.debug("Inside of the setModelProperty(String,Object) method...");
        if ( children != null ) {
            for ( IController controller : children ) {
                try {
                    Method method = controller.getModel().getClass().getMethod(
                        "set" + propertyName,
                        new Class[] { newValue.getClass() });
                    method.invoke(controller, newValue);

                } catch ( Exception ex ) {
                    log
                        .debug("Inside of the setModelProperty(String,Object) method... Exception Occured");
                    // Do nothing...
                }
            }
        }
        // does this controller have a model? if so, then mod it...
        try {
            Method method = model.getClass().getMethod("set" + propertyName,
                new Class[] { newValue.getClass() });
            method.invoke(model, newValue);
        } catch ( Exception ex ) {
            log.debug("Exception Occured:"+ex.getMessage());
            // Do nothing...
        }
    }
}
